// prototype __proto__ constructor
// es5中没有类,只有构造函数(函数名首字母大写)来模拟类

function Animal(){
  this.type = '哺乳类'; //实例上的属性

  // 如果返回一个普通值，则不会影响new Animal()的返回值
  // return 'hello';

  //但如果返回的是一个引用类型，则new Animal()咋不再返回实例，而是这个指定的引用
  // return {
  //   a:1
  // }
}
//公共属性和方法
Animal.prototype.gongyoushuxing = {};
Animal.prototype.gongyoufangfa= function(){};
//静态属性和方法
Animal.jingtaishuxing = 'a';
Animal.jingtaifangfa = function(){};

let animal = new Animal();
console.log(animal);

//每个对象上都有一个属性 __proto__ (原型链) 指向它所属类的原形 x.prototype (这样这个对象就能拿到被指向对象上的属性和方法)
//一个类的原形 __proto__(指向) Object对象的原形 Object.prototype
console.log(animal.__proto__ === Animal.prototype); //true
console.log(Animal.prototype.__proto__ === Object.prototype); //true
console.log(Object.prototype.__proto__); //null，到根了

console.log(Object.__proto__ === Function.__proto__); //true ， 都指向Function.prototype

console.log(Object.__proto__ === Function.prototype); //true
console.log(Function.__proto__ === Function.prototype); //true
console.log(typeof Function.prototype); //function，而其它的原形是对象类型

console.log(Function.prototype.__proto__); //{}
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype);
/*
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
*/

//每个原型上都一个constructor属性，指向它所属的类
console.log(Animal.prototype.constructor === Animal); //true
console.log(animal.constructor === Animal); //true


//如何实现继承？
Animal.prototype.eat = function () {
  console.log('this:',this);
  console.log('吃饭');
};
function Tiger(){
  Animal.call(this);
}


//这样修改子类的原形会污染父类的原形，这不是我们所期望的↓
// Tiger.prototype = Animal.prototype;
// Tiger.prototype.x = 'x'; //因为是引用，so会影响到Animal自己的原形


//法一：
// Tiger.prototype.__proto__ = Animal.prototype;
//↑↓等价
Object.setPrototypeOf(Tiger.prototype, Animal.prototype);
// Tiger.prototype.x = 'x';  //TODO 通过原型链而不是引用的方式，修改原形链左侧的对象的属性并不会影响到右侧


//法二：
function create(Pproto){ //Pproto父类的原形
  let Fn = function () {

  };
  Fn.prototype = Pproto;
  return new Fn();
}
// Tiger.prototype = create(Animal.prototype);
//↑↓等价
Tiger.prototype = Object.create(Animal.prototype,{constructor:{value:Tiger,writable:true,configurable:true}}); //如果传递了constructor参数，会给当前的new fn用Object.defineProperty定义个constructor属性 并指向你传递的constructor的value
console.log('Tiger.prototype.hasOwnProperty(\'constructor\'):',Tiger.prototype.hasOwnProperty('constructor')); //true
// Tiger.prototype.x = 'x'; //这样的话，修改只会影响到中间代理类Fn的实例，而不会影响到父类

let tiger = new Tiger();
tiger.eat();
console.log('tiger.constructor:',tiger.constructor); // 默认指向了Animal，需要Object.create时搭配constructor参数
// let eat = tiger.eat;
// eat(); //浏览器中就是window;另外如果是es6的类这样用 就指向undefined

